home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Best of MacTutor - S…e Code for Volumes 1 to 5
/
The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin
/
Source Code
/
#30 (Mar 88)
/
Quicktraps
/
quicktrap.a
Wrap
Text File
|
1987-10-24
|
8KB
|
227 lines
; Macintosh Toolbox and OS-trap bypass routines.
; Copyright © 1987 Michael S. Morton
;
; History: 10-May-87 - MM - Initial version.
; 22-Oct-87 - MM - Neatened for publication.
BLANKS ON
STRING ASIS
PRINT OFF
LOAD 'tlAsmSyms.sym'
PRINT ON
; Impure code! Should be in a segment which is locked in memory.
SEG 'LOCKDSEG' ; *** change to a locked segment ***
;--------------------------------------------------------------------------
; Each Toolbox routine starts out life as:
; 2 <pure copy of trap word>
; entry: 2 <trap word with auto-pop bit>
; 4 <four bytes unused>
;
; The evaluator changes this to:
; 2 <pure copy of trap word>
; entry: 6 JMP <actual address>
;
; Either form is callable with a JSR because the former includes the “auto-pop”
; bit, so the Toolbox routine returns to its caller’s caller. Offsets are:
tTrap: EQU 0 ; pure copy of trap
tJump: EQU 2 ; JMP xxx.L instruction
tAddress: EQU 4 ; address to jump to
tLength: EQU 8 ; length of one block
; The “qtTool” macro generates code for one “qt” Toolbox routine.
;
; args: routine - Name for routine. Typically “qt” plus trap name.
; trap - Name of the trap, eg, “_MoveTo”.
MACRO
qtTool
EXPORT &Syslst[1] ; define the “qtXXX” routine globally
&Syslst[2] ; first, a pure copy of the trap word
&Syslst[1] &Syslst[2] autoPop ; entry: if not overwritten, just trap
ds.b 4 ; reserve room for overwriting
ENDM
;--------------------------------------------------------------------------
; Each OS trap bypass routine is 28 bytes long. The unevaluated routine is:
; 2 <pure copy of trap word>
; entry: 2 <trap word> ; only this part…
; 2 RTS ; …gets executed before eval
; 4 MOVE.W #<xxx>, D1 ; trap word patched here
; 4 MOVE.W #<xxx>, D2 ; trap number patched here
; 6 JSR xxx.L ; routine address patched here
; 4 MOVEM.L (SP)+, D1-D2/A1-A2 ; register list patched here
; 2 TST.W D0 ; set condition codes
; 2 RTS ; and return
; After the evaluator is done, the routine becomes:
; 2 <pure copy of trap word>
; entry: 4 MOVEM.L D1-D2/A1-A2, -(SP) ; (saves A0 too, if bit 8 set)
; 4 MOVE.W #<trapword>, D1 ; get trap word in D1
; 4 MOVE.W #<trapword & $01FF>, D2 ; …and trap number
; 6 JSR xxx.L ; call the routine
; 4 MOVEM.L (SP)+, D1-D2/A1-A2 ; (gets A0 too, if bit 8 set)
; 2 TST.W D0 ; set condition codes
; 2 RTS ; and return
oTrap: EQU 0 ; original trap word
oSave: EQU 2 ; for MOVEM.L xxx, -(SP) to go
oTrapWord: EQU 8 ; for trap word in MOVE.W #xxx, D1
oTrapNum: EQU 12 ; for trap number in MOVE.W #xxx, D2
oAddress: EQU 16 ; address to jump to
oRestore: EQU 22 ; for second copy of MOVEM regs list
oLength: EQU 28 ; length of one block
; The “qtOS” macro generates code for one “qt” OS routine.
;
; args: routine - Name for trap routine. Typically “qt” plus trap name.
; trap - Name of the trap, eg, “_GetHandleSize”.
MACRO
qtOS
EXPORT &Syslst[1] ; define the “qtXXX” routine globally
&Syslst[2] ; pure copy of trap word
&Syslst[1] &Syslst[2] ; entry: if not overwritten, just trap…
RTS ; …and return
MOVE.W #$5555, D1 ; get trap word in D1, for OS routine
MOVE.W #$5555, D2 ; …and trap number
JSR $55555555 ; leave space for a longword address
MOVEM.L (SP)+, D1-D2/A1-A2 ; assume A0 not in the register list
TST.W D0 ; set condition codes
RTS ; and return
ENDM
;--------------------------------------------------------------------------
; Resource type and ID for the flag used to disable the trickery.
qtrpType: EQU 'QTRP' ; resource type for flag
qtrpId: EQU 257 ; resource ID for flag
; qtEval
;
; description:
; Update the routines so they jump directly into the ROM, or wherever.
; This routine should be called at startup, and each time the application
; thinks anyone has (or might have) called SetTrapAddress.
;
; uses: (no registers)
qtEval: PROC EXPORT
; Stuff used in patching together routines:
jmpInst: EQU $4EF9 ; opcode word of JMP xxx.L
OSregs: REG D1-D2/A1-A2 ; registers saved by OS dispatcher
OSregs2:REG D1-D2/A0-A2 ; registers saved when bit 8 is zero
MOVEM.L D0-D2/A0-A2, -(SP) ; save caller’s registers
; First, see if we’ve already been told not to do our thing:
LEA qtEnabled, A2 ; point to the flag
TST.B (A2) ; have we snuffed it already?
BEQ evalEnd ; yes: nothing to do
; Second, decide if the resource flag allows us to map/cache.
SUBQ #4, SP ; make room for function result
MOVE.L #qtrpType, -(SP) ; pass the type…
MOVE.W #qtrpId, -(SP) ; …and ID
_GetResource ; try to find our flag
MOVE.L (SP)+, A0 ; pop result…
MOVE.L A0, D0 ; …and test for NIL
BEQ.S evalTurnOff ; no such thing? go flag this and exit
MOVE.L (A0), A1 ; deref. handle; point to rsrc with A0
MOVE.W (A1), D2 ; pick up first word, to check later
MOVE.L A0, -(SP) ; pass handle…
_ReleaseResource ; …and get rid of it
TST.W D2 ; now check -- did rsrc start with zero?
BNE.S evalTurnOff ; …no: we don’t yet do selective disable
; Nothing forbids hackery. Evaluate all the toolbox bypass routines.
LEA qtToolStart, A1 ; point to first routine
LEA qtToolEnd, A2 ; point to just after last routine
MOVE.W #jmpInst, D1 ; get a JMP xxx.L instruction
BRA.S toolEnd ; check for no routines
toolLp: MOVE.W tTrap(A1), D0 ; pick up the trap number in D0.w
_GetTrapAddress newTool ; ask where this routine lives
MOVE.L A0, tAddress(A1) ; store address first, THEN…
MOVE.W D1, tJump(A1) ; …the JMP, so routine’s always OK
ADDQ #tLength, A1 ; advance to the next routine
toolEnd:CMP.L A2, A1 ; at (or past) end of toolbox routines?
BLO.S toolLp ; nope: go evaluate another one
; Evaluate all the OS bypass routines.
LEA qtOSStart, A1 ; point to first…
LEA qtOSEnd, A2 ; …and to just after last
BRA.S osEnd ; handle degenerate case
osLoop: MOVE.W oTrap(A1), D0 ; pick up trap number
MOVE.W D0, D2 ; copy it for later use (BTST, etc.)
_GetTrapAddress newOS ; find where the routine lives
MOVE.L A0, oAddress(A1) ; save routine address in JMP xxx.L
MOVE.W D2, oTrapWord(A1) ; fill in MOVE.W #trapword, D1
AND.W #$01FF, D2 ; get just the trap number
MOVE.W D2, oTrapNum(A1) ; and store that in MOVE.W #trapnum, D2
; Decide whether the saved registers include A0.
MOVE.L OSent, D0 ; assume we want usual registers saved…
MOVE.L OSexit, D1 ; …and restored
BTST #8, D2 ; but should we save A0, too?
BNE.S osLp1 ; nope: OSent’s registers are fine
MOVE.L OSent2, D0 ; yep: use reg list which includes A0
MOVE.L OSexit2, D1 ; …and ditto for one which saves A0
osLp1: MOVE.W D1, oRestore(A1) ; store register list for restore
MOVE.L D0, oSave(A1) ; lastly, get rid of 1st trap
ADD #oLength, A1 ; advance to the next routine
osEnd: CMP.L A2, A1 ; at the end?
BLO.S osLoop ; nope: go do another
evalEnd:MOVEM.L (SP)+, D0-D2/A0-A2 ; restore caller’s registers
RTS
; Here when the resource forbids caching. A2 points to the flag.
evalTurnOff:
SF (A2) ; …disable it for faster call next time
BRA.S evalEnd ; clean up and exit
; *** Impure *** flag: 0 means mapping disabled; non-zero means enabled.
qtEnabled:
DC.B $FF,00 ; initially enabled; 2nd byte to align
; Instructions and register lists to stick into OS routines. Each is 2 words.
OSent: MOVEM.L OSregs, -(SP)
OSent2: MOVEM.L OSregs2, -(SP)
OSexit: MOVEM.L (SP)+, OSregs
OSexit2:MOVEM.L (SP)+, OSregs2
; Toolbox trap replacement routines. To be re-evaluated, these must be between
; qtToolStart and qtToolEnd. Nothing else must be in here -- the evaluator
; walks through this as an array.
qtToolStart: ; Beginning of Toolbox trap replacement routines.
qtTool qtMoveTo,_MoveTo
qtTool qtSetPort,_SetPort
; …add your own here…
qtToolEnd: ; End of Toolbox trap replacement routines.
; OS trap replacement routines. As with toolbox, keep only these in here.
qtOSStart: ; Beginning of OS trap replacement routines.
qtOS qtHLock,_HLock
qtOS qtHUnlock,_HUnlock
; …add your own here…
qtOSEnd: ; End of OS trap replacement routines.
END